package com.timing.finder.framework;

import com.example.autojob.skeleton.annotation.AutoJobScan;
import com.example.autojob.skeleton.db.mapper.AutoJobMapperHolder;
import com.example.autojob.skeleton.enumerate.SchedulingStrategy;
import com.example.autojob.skeleton.framework.boot.AutoJobApplication;
import com.example.autojob.skeleton.framework.boot.AutoJobBootstrap;
import com.example.autojob.skeleton.framework.config.AutoJobRetryConfig;
import com.example.autojob.skeleton.framework.task.AutoJobTask;
import com.example.autojob.skeleton.model.builder.AttributesBuilder;
import com.example.autojob.skeleton.model.builder.AutoJobMethodTaskBuilder;
import com.example.autojob.skeleton.model.task.method.MethodTask;
import com.example.autojob.util.thread.SyncHelper;
import com.timing.finder.util.RedisUtil;
import com.timing.finder.util.StringUtils;
import com.timing.finder.util.id.IdGenerator;
import com.timing.finder.util.servlet.InetUtil;
import lombok.extern.slf4j.Slf4j;

import java.util.Collections;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @author JingGe(* ^ ▽ ^ *)
 * @date 2023-03-07 11:33
 * @email 1158055613@qq.com
 */
@Slf4j
@AutoJobScan("com.sccl")
public class TimingFinderApplication<T extends TimeSeriesData> {
    HandleType handleType;

    String applicationName;

    TimingFinderConfigFactory configFactory;

    TimeSeriesDataHandler<T> handler;

    TimeSeriesDataLoader<T> loader;

    DataLoadTimingJob loadTimingJob;

    DBRepository<T> dbRepository;

    TimingFinderConfig config;

    StorableBloomFilter bloomFilter;

    TimeSeriesDataFinder<T> finder;

    TimingFinderApplication() {
    }

    public TimingFinderApplication<T> run() {
        Thread runThread = new Thread(() -> {
            try {
                if (RedisUtil.getRedisTemplate() == null && handler.cacheRepository() instanceof RedisRepository) {
                    throw new UnsupportedOperationException("应用未启用默认的Redis存储，也未设置其他Cache存储");
                }
                TimingFinderApplicationContext
                        .getInstance()
                        .init(this);
                DataLoadTimingJobContext
                        .getInstance()
                        .init(applicationName, new DataLoadTimingJobContext.Context(config.starID, System.currentTimeMillis(), 0L));
                if (config.enableLoader) {
                    //加载Cache中的运行状态
                    if (!config.clearBeforeRun) {
                        String json = handler
                                .cacheRepository()
                                .get(TimingFinderConstant.STATUS_KEY + ":" + config.applicationName, String.class);
                        if (!StringUtils.isEmpty(json)) {
                            DataLoadTimingJobContext
                                    .getInstance()
                                    .update(applicationName, json);
                            log.info("恢复状态：{}", json);
                            config.starID = DataLoadTimingJobContext
                                    .getInstance()
                                    .getCurrentID(applicationName);
                        }
                    }
                    log.info("{}应用本次加载起始ID：{}，截止ID：{}", applicationName, config.starID, config.endID);
                    try {
                        new AutoJobBootstrap(this.getClass(), "auto-job-default.yml", "auto-job.yml")
                                .withAutoScanProcessor()
                                .setEnv("${env}")
                                .build()
                                .run();
                    } catch (IllegalStateException ignored) {
                    }
                    SyncHelper.aWaitQuietly(() -> AutoJobApplication
                            .getInstance()
                            .isRunning());
                    AutoJobMapperHolder.METHOD_TASK_ENTITY_MAPPER.saveTasks(Collections.singletonList(task()));
                } else {
                    log.warn("暂未开启时序数据同步，不会新建任务");
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
        runThread.setName(String.format("%s-runThread", applicationName));
        runThread.start();
        return this;
    }

    public HandleType getHandleType() {
        return handleType;
    }

    public String getApplicationName() {
        return applicationName;
    }

    public TimeSeriesDataFinder<T> getTimeSeriesDataFinder() {
        if (finder == null) {
            synchronized (TimeSeriesDataFinder.class) {
                if (finder == null) {
                    finder = new TimeSeriesDataFinder<>(handler);
                }
            }
        }
        return finder;
    }

    private MethodTask task() {
        return new AutoJobMethodTaskBuilder(YamlConfigTimingJob.class, "run")
                .setTaskId(IdGenerator.getNextIdAsLong())
                .setAnnotationID((long) Math.abs((applicationName + applicationName).hashCode()))
                .addACronExpressionTrigger(config.cronExpression, config.repeatCount)
                .setTaskType(AutoJobTask.TaskType.DB_TASK)
                .setMaximumExecutionTime((long) (config.maximumExecutionTime * 60 * 1000), TimeUnit.MILLISECONDS)
                .addParam(AttributesBuilder.AttributesType.STRING, applicationName)
                .addParam(AttributesBuilder.AttributesType.BOOLEAN, config.enableLoader)
                .addExecutableMachine(Objects.requireNonNull(InetUtil.getLocalhostIp()))
                .setSchedulingStrategy(SchedulingStrategy.JOIN_SCHEDULING)
                .setRetryConfig(new AutoJobRetryConfig(true, 10, 5))
                .setMethodObjectFactory(new YamlConfigTimingJob.TimingJobObjectFactory())
                .setTaskAlias("时序数据加载任务")
                .build();
    }
}
